package com.ansion.basedb.dao.helper;

import androidx.annotation.NonNull;

import com.ansion.basedb.database.ISqlDataBase;
import com.ansion.basedb.database.sql.SQLField;
import com.ansion.basedb.database.sql.SQLTable;
import com.ansion.basedb.database.sql.SQLUnionIndex;
import com.ansion.basetool.preconditions.Preconditions;
import com.ansion.log.S;
import com.ansion.log.VLog;
import com.ansion.utils.DynamicStringBuilder;
import com.ansion.utils.ValueSafeTransfer;

import java.util.ArrayList;
import java.util.List;

public class SqlTableHelper {

    public static void createTable(net.zetetic.database.sqlcipher.SQLiteDatabase db, @NonNull List<SQLTable> tables, int version) {
        Preconditions.checkNotNull(tables);
        Preconditions.checkNotEmpty(tables);
        ValueSafeTransfer.iterateAll(tables, (position, table) -> ValueSafeTransfer.iterateAll(_create(table, version), (p, sql)
                -> db.execSQL(sql)));
    }

    public static void createTable(android.database.sqlite.SQLiteDatabase db, @NonNull List<SQLTable> tables, int version) {
        Preconditions.checkNotNull(tables);
        Preconditions.checkNotEmpty(tables);
        ValueSafeTransfer.iterateAll(tables, (position, table) -> ValueSafeTransfer.iterateAll(_create(table, version), (p, sql)
                -> db.execSQL(sql)));
    }

    public static void createTable(ISqlDataBase db, @NonNull List<SQLTable> tables, int version) {
        Preconditions.checkNotNull(tables);
        Preconditions.checkNotEmpty(tables);
        ValueSafeTransfer.iterateAll(tables, (position, table) -> ValueSafeTransfer.iterateAll(_create(table, version), (p, sql)
                -> db.execSQL(sql)));
    }


    private static List<String> _create(@NonNull SQLTable table, int versionCode) {
        List<String> sqlStrings = new ArrayList<>();
        if (table.getVersionCode() <= versionCode) {
            String tableName = table.getTableName();
            List<SQLField> columns = table.getFields();
            boolean ftsTable = table.isFts();
            boolean mutPrimaryKey = table.isMutPrimaryKey();
            DynamicStringBuilder sql = new DynamicStringBuilder();
            sql.append("CREATE ")
                    .appendIf(ftsTable, " VIRTUAL ")
                    .append(" TABLE IF NOT EXISTS ")
                    .append(tableName)
                    .appendIf(ftsTable, " USING fts5 ")
                    .append("(")
                    .append(generateCreateFields(columns, versionCode, ftsTable, mutPrimaryKey))
                    .appendIf(ftsTable, ", tokenize = trigram")
                    .append(")");
            VLog.i("TAG_DB_SQLITE", "SQLDBHelper.createTable sql:" + sql);
            S.s("_create: " + sql);
            sqlStrings.add(sql.toString());
            for (SQLField indexField : columns) {
                if (indexField.isIndex() && !ftsTable) {
                    sqlStrings.add(generateIndex(tableName, indexField));
                }
            }
            for (SQLUnionIndex unionIndex : table.getUnionIndexes()) {
                String indexSQL = unionIndex.buildIndexSQL();
                VLog.i("TAG_DB_SQLITE", "SQLDBHelper.createTable sql:" + indexSQL);
                S.s("_create: " + indexSQL);
                sqlStrings.add(indexSQL);
            }
        }
        return sqlStrings;
    }


    public static void upgradeTable(net.zetetic.database.sqlcipher.SQLiteDatabase db, List<SQLTable> tables, int oldVersion, int newVersion) {
        Preconditions.checkNotNull(tables);
        Preconditions.checkNotEmpty(tables);
        ValueSafeTransfer.iterateAll(_upgrade(tables, oldVersion, newVersion), (position, sql)
                -> db.execSQL(sql));
    }

    public static void upgradeTable(android.database.sqlite.SQLiteDatabase db, List<SQLTable> tables, int oldVersion, int newVersion) {
        Preconditions.checkNotNull(tables);
        Preconditions.checkNotEmpty(tables);
        ValueSafeTransfer.iterateAll(_upgrade(tables, oldVersion, newVersion), (position, sql)
                -> db.execSQL(sql));
    }


    public static List<String> _upgrade(List<SQLTable> tables, int oldVersion, int newVersion) {
        List<String> sqlStrings = new ArrayList<>();
        for (SQLTable table : tables) {
            String tableName = table.getTableName();
            int versionCode = table.getVersionCode();
            //表没有创建过，create。
            if (versionCode > oldVersion) {
                if (versionCode <= newVersion) {
                    sqlStrings.addAll(_create(table, newVersion));
                }
            } else {
                //FTS table alter is not supported.
                if (table.isFts()) {
                    S.e("fts can not upgrade");
                    continue;
                }
                //表已经创建，upgrade。
                for (SQLField field : table.getFields()) {
                    if (field.getVersionCode() > oldVersion && field.getVersionCode() <= newVersion) {
                        DynamicStringBuilder sql = new DynamicStringBuilder();
                        sql.append("ALTER TABLE ")
                                .append(tableName)
                                .append(" ADD COLUMN ")
                                .append(generateField(field, false, false));
                        VLog.i("TAG_DB_SQLITE", "SQLDBHelper.upgradeTable sql:" + sql);
                        S.s("upgrade: " + sql);
                        sqlStrings.add(sql.toString());
                        if (field.isIndex()) {//如果是索引项，则把创建索引给加上去
                            sqlStrings.add(generateIndex(tableName, field));
                        }
                    }
                }
                for (SQLUnionIndex unionIndex : table.getUnionIndexes()) {
                    if (unionIndex.getVersionCode() > oldVersion && unionIndex.getVersionCode() <= newVersion) {
                        String indexSQL = unionIndex.buildIndexSQL();
                        VLog.i("TAG_DB_SQLITE", "SQLDBHelper.createTable sql:" + indexSQL);
                        S.s("upgrade: " + indexSQL);
                        sqlStrings.add(indexSQL);
                    }
                }
            }
        }
        return sqlStrings;
    }

    private static String generateField(@NonNull SQLField column, boolean ftsTable, boolean mutPrimaryKey) {
        return new DynamicStringBuilder(column.getName())
                .append(" ")
                .appendIf(ftsTable && !column.isIndex(), " UNINDEXED ")
                .appendIf(!ftsTable, SQLExecuteHelper.getType(column.getType()))
                .appendIf(!ftsTable && !mutPrimaryKey && column.isPrimaryKey(), " PRIMARY KEY ")
                .appendIf(!ftsTable && !mutPrimaryKey && column.isPrimaryKey() && column.isAutoIncrement(), " AUTO_INCREMENT ")
                .appendIf(!ftsTable && !mutPrimaryKey && !column.isPrimaryKey() && column.isNonNull(), " NOT NULL ")
                .appendIf(!ftsTable && !mutPrimaryKey && !column.isPrimaryKey() && column.isUnique(), " UNIQUE ")
                .toString();
    }


    private static String generateCreateFields(@NonNull List<SQLField> columns, int versionCode, boolean ftsTable, boolean mutPrimaryKey) {
        DynamicStringBuilder fieldsBuilder = new DynamicStringBuilder();
        List<SQLField> mutPrimaryKeyColumns = new ArrayList<>();
        for (int i = 0; i < columns.size(); i++) {
            SQLField field = columns.get(i);
            if (field.getVersionCode() > versionCode) {
                continue;
            }
            if (mutPrimaryKey && field.isPrimaryKey()) {
                mutPrimaryKeyColumns.add(field);
            }
            fieldsBuilder.appendIf(fieldsBuilder.length() > 0, " , ")
                    .append(generateField(field, ftsTable, mutPrimaryKey));
        }
        if (mutPrimaryKey) {
            if (mutPrimaryKeyColumns.size() < 2)
                throw new IllegalArgumentException("mutPrimaryKey must have over 2 primaryKey column,your primaryKey columns is less than 2");
            //追加联合主键
            fieldsBuilder.append(",PRIMARY KEY(");
            for (int i = 0; i < mutPrimaryKeyColumns.size(); i++) {
                SQLField field = mutPrimaryKeyColumns.get(i);
                fieldsBuilder.append(field.getName());
                if (i != mutPrimaryKeyColumns.size() - 1) {
                    fieldsBuilder.append(",");
                }
            }
            fieldsBuilder.append(")");
        }


        return fieldsBuilder.toString();
    }

    private static String generateIndex(@NonNull String tableName, @NonNull SQLField column) {
        return "create index if not exists index_" + column.getName() + "_" + tableName + " on " + tableName + "(" + column.getName() + ");";
    }
}
